package com.woniuxy.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.woniu.zsd.commons.core.exception.ZsdException;
import com.woniuxy.user.entity.UserInfo;
import com.woniuxy.user.form.AddUserinfoForm;
import com.woniuxy.user.form.UserInfoForm;
import com.woniuxy.user.mapper.UserAddressesMapper;
import com.woniuxy.user.mapper.UserInfoMapper;
import com.woniuxy.user.param.*;
import com.woniuxy.user.service.IUserInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniuxy.user.utils.JwtUtils;
import com.woniuxy.user.utils.MD5Util;
import com.woniuxy.user.utils.OssUtil;
import com.woniuxy.user.utils.SMSUtil;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户详情表 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-05-13
 */
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements IUserInfoService {
    @Resource
    private UserInfoMapper userInfoMapper;
    @Resource
    private SMSUtil smsUtil;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private UserAddressesMapper userAddressesMapper;
    @Resource
    private OssUtil ossUtil;


    @Override
    public UserInfo userPsswordLogin(UserPasswordLoginParam param) throws Exception {
        LambdaQueryWrapper<UserInfo> QueryWrapper = new LambdaQueryWrapper<>();
        QueryWrapper.eq(UserInfo::getLoginName, param.getLoginName());
        UserInfo userInfo = userInfoMapper.selectOne(QueryWrapper);
        //前置判断
        if (userInfo == null) {
            throw new RuntimeException("用户不存在");
        }

        /*
          加MD5
         */
        String md5 = MD5Util.md5(param.getPassword(), param.getLoginName());
        System.out.println(md5);
        if (!userInfo.getPassword().equals(md5)) {
            throw new RuntimeException("密码错误");
        }
        //获取当前服务器时间
        LocalDateTime now = LocalDateTime.now();

        //更新登陆时间
        userInfo.setLastLoginTime(now);
        userInfoMapper.updateById(userInfo);
        return userInfo;
    }

    @Override
    public void register(AddUserinfoForm form) throws Exception {
        //判断code于用户输入的code是否一致
        String code = stringRedisTemplate.opsForValue().get("login:code:" + form.getPhone());
        if (!code.equals(form.getCode())) {
            throw new RuntimeException("验证码错误");
        }
//        if (stringRedisTemplate.hasKey("login:interval:phone"+form.getPhone())){
//            throw new RuntimeException("请勿频繁发送验证码");
//        }
        //验证码一致就成功注册，默认密码为111
        UserInfo userInfo = new UserInfo();
        //用户的登录名字就是
        userInfo.setLoginName(form.getPhone());
        //手机号塞入数据库
        userInfo.setPhone(form.getPhone());
        //用户昵称为“用户”+手机号后4位
        userInfo.setNickName("用户" + form.getPhone().substring(form.getPhone().length() - 4));
        //默认密码为111
        userInfo.setPassword(MD5Util.md5("111", form.getPhone()));
        //默认status为0，默认为正常状态
        userInfo.setStatus(0);
        userInfoMapper.insert(userInfo);
        stringRedisTemplate.delete("login:code:" + form.getPhone());

    }

    @Override
    public void registerCode(String phone) throws Exception {
        String code = smsUtil.sendCode(phone);
        stringRedisTemplate.opsForValue().set("login:interval:phone" + phone, "", 1, TimeUnit.MINUTES);
        stringRedisTemplate.opsForValue().set("login:code:" + phone, code, 5, TimeUnit.MINUTES);
        rabbitTemplate.convertAndSend("login_code_queue", phone);
    }


    @Override
    public UserInfo loginCodePhone(UserLoginCodeParam bean) throws Exception {
        // 判断手机号是否存在
        UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, bean.getPhone()));
        if (userInfo == null) {
            throw new RuntimeException("用户不存在");
        }
        //从redis中获取验证码并且对比是否一致
        //从redis中获取验证码判断
        String codeFromRedis = stringRedisTemplate.opsForValue().get("login:code:" + bean.getPhone());
        if (codeFromRedis == null || !codeFromRedis.equals(bean.getCode())) {
            throw new Exception("验证码错误");
        }

        //获取当前系统时间并塞入user_info表的last_login_time字段
        LocalDateTime now = LocalDateTime.now();
        userInfoMapper.update(new UserInfo().setLastLoginTime(now), new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, bean.getPhone()));


        return userInfo;
    }

    @Override
    public void loginCode(String phone) throws Exception {
//        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey("login:interval:phone"+phone))){
//            throw new RuntimeException("请勿频繁发送验证码");
//        }
        String code = smsUtil.sendCode(phone);
        stringRedisTemplate.opsForValue().set("login:interval:phone" + phone, "", 1, TimeUnit.MINUTES);
        stringRedisTemplate.opsForValue().set("login:code:" + phone, code, 5, TimeUnit.MINUTES);
        rabbitTemplate.convertAndSend("login_code_queue", phone);
    }



    @Override
    public void updatePassword(UpdatePasswordParam param ,String token) throws Exception {
        //拿到用户的登录名
        String loginName = JwtUtils.parseToken(token).get("loginName").toString();
        //塞入param的salt
        param.setSalt(loginName);
        //从token中获取用户id
        String id = JwtUtils.parseToken(token).get("id").toString();

        // 查询用户是否存在（通过用户 ID 查询）
        UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, id));
        if (userInfo == null) {
            throw new ZsdException("用户不存在", 1002);
        }
        // 判断旧密码是否正确
        String oldPasswordMd5 = MD5Util.md5(param.getOldLoginPwd(), param.getSalt()); // 使用数据库中存储的盐值进行MD5加密
        if (!oldPasswordMd5.equals(userInfo.getPassword())) {
            throw new ZsdException("旧密码错误", 1003);
        }
        //输出新密码加密后的
        System.out.println("cdacochquohcnqjuibjk"+ MD5Util.md5(param.getNewLoginPwd(), param.getOldLoginPwd()));
        //将新密码存储到数据库中
        userInfoMapper.update(new UserInfo().setPassword(MD5Util.md5(param.getNewLoginPwd(), param.getSalt())), new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, id));
    }

    @Override
    public void updatePhoto(MultipartFile photo, int id) throws Exception {
        String photo1 = ossUtil.upload("woniuxy", "photo", photo.getInputStream());
        //塞进user_info表中的head_img
        userInfoMapper.update(new UserInfo().setHeadImg(photo1), new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, id));

    }


    @Override
    public UserInfoForm userInfo(int id) throws Exception {
        // 使用 MyBatis-Plus 的 selectById 方法查询用户信息
        UserInfo userInfo = userInfoMapper.selectById(id);
        if (userInfo == null) {
            throw new ZsdException("用户不存在", 1005);
        }
        //将userInfo对象转换为UserInfoForm对象
        UserInfoForm userInfoForm = new UserInfoForm();
        userInfoForm.setPhone(userInfo.getPhone());
        userInfoForm.setNickName(userInfo.getNickName());
        userInfoForm.setHeadImg(userInfo.getHeadImg());
        userInfoForm.setLoginName(userInfo.getLoginName());
        userInfoForm.setPoint(userInfo.getPoint());

        return userInfoForm;
    }

    @Override
    public void updateLoginName(UpdateLoginNameParam param,String token) throws Exception {
        // 从token中拿到id
        String id = JwtUtils.parseToken(token).get("id").toString();
        // 1. 检查用户是否存在
        UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getId, id)); // 假设 param 中包含用户 ID
        if (userInfo == null) {
            throw new ZsdException("用户不存在", 1007);
        }

        // 2. 检查新登录名是否唯一（排除当前用户）
        Long count = userInfoMapper.selectCount(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getLoginName, param.getLoginName())
                .ne(UserInfo::getId, id)); // 排除当前用户
        if (count > 0) {
            throw new ZsdException("登录名已存在", 1006);
        }
        //检查密码是否一致md5
        if ( !userInfo.getPassword().equals(MD5Util.md5(param.getPassword(), userInfo.getLoginName()))) {
            throw new ZsdException("密码错误", 1008);
        }
        // 4. 使用用户输入的当前密码加上新的登录名重新加密
        String newEncryptedPassword = MD5Util.md5(param.getPassword(), param.getLoginName());

        // 5. 更新用户信息
        userInfo.setLoginName(param.getLoginName());
        userInfo.setPassword(newEncryptedPassword);
        userInfoMapper.updateById(userInfo);


    }

    @Override
    public void updateNickName( String id,String nickName) throws Exception {
        //根据id为条件直接修改昵称
        userInfoMapper.update(new UserInfo().setNickName(id), new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, id));
    }

    @Override
    public void changeBindingSendCode(String phone) throws Exception {
        //如果用户不存在则抛出异常
        UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, phone));
        if (userInfo == null) {
            throw new ZsdException("用户不存在", 1002);
        }
        //判断手机号是否唯一如果存在则抛出异常
        if (userInfoMapper.selectCount(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, phone)) > 0) {
            throw new ZsdException("手机号已存在", 1009);
        }
        // 调用工具类发送验证码
        String code = smsUtil.sendCode(phone);
        // 在Redis中设置换绑验证码的间隔限制
        stringRedisTemplate.opsForValue().set("change_binding:interval:phone" + phone, "", 1, TimeUnit.MINUTES);
        // 在Redis中存储换绑验证码，设置过期时间为5分钟
        stringRedisTemplate.opsForValue().set("change_binding:code:" + phone, code, 5, TimeUnit.MINUTES);
        // 发送消息到RabbitMQ队列，队列名称可以根据实际需求修改
        rabbitTemplate.convertAndSend("change_binding_code_queue", phone);

    }

    @Override
    public void updatePhone(UpdatePhoneParam bean ,String token) throws Exception {
        // 从token中拿到id
        String id = JwtUtils.parseToken(token).get("id").toString();
        //先判断手机号是否唯一
        if (userInfoMapper.selectCount(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, bean.getPhone())) > 0) {
            throw new ZsdException("手机号已存在", 1009);
        }
        //判断验证码以及对应手机号码是否正确
        if (!stringRedisTemplate.opsForValue().get("change_binding:code:" + bean.getPhone()).equals(bean.getCode())) {
            throw new ZsdException("验证码错误", 1010);
        }

        stringRedisTemplate.delete("change_binding:code:" + bean.getPhone());
        stringRedisTemplate.delete("change_binding:interval:phone" + bean.getPhone());

        //成功就直接根据id修改
        userInfoMapper.update(new UserInfo().setPhone(bean.getPhone()), new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, id));

    }

    @Override
    public void updatePasswordCode(String phone , String token) throws Exception {
        // 根据token获取用户信息
        String id = JwtUtils.parseToken(token).get("id").toString();
        //根据id查询出对应的手机号不匹配的话就不能发送
        UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, id));
        if (!userInfo.getPhone().equals(phone)) {
            throw new ZsdException("不是你绑定的手机号，请确认手机号", 1002);
        }
        // 检查是否在短时间内重复发送验证码，避免频繁发送
        String key = "update_password:interval:" + phone;
        if (stringRedisTemplate.hasKey(key)) {
            throw new ZsdException("验证码发送过于频繁，请稍后再试", 1010);
        }

        // 调用工具类发送验证码
        String code = smsUtil.sendCode(phone);

        // 在Redis中存储修改密码验证码，设置过期时间为5分钟
        stringRedisTemplate.opsForValue().set("update_password:code:" + phone, code, 5, TimeUnit.MINUTES);

        // 在Redis中设置验证码发送间隔限制，例如1分钟内只能发送一次
        stringRedisTemplate.opsForValue().set(key, "", 1, TimeUnit.MINUTES);

        // 发送消息到RabbitMQ队列，队列名称可以根据实际需求修改
        rabbitTemplate.convertAndSend("update_password_code_queue", phone);
    }

    @Override
    public void updatePasswordCodeX(UpdatePasswordCodeParam bean, String token) throws Exception {
        // 判断验证码和对应手机号码是否正确
        if (!Objects.equals(stringRedisTemplate.opsForValue().get("update_password:code:" + bean.getPhone()), bean.getCode())) {
            throw new ZsdException("验证码错误", 1010);
        }
        stringRedisTemplate.delete("update_password:code:" + bean.getPhone());
        stringRedisTemplate.delete("update_password:interval:" + bean.getPhone());
        // 根据token获取用户信息
        String id = JwtUtils.parseToken(token).get("id").toString();
        //根据id查询出登录名 ，然后md5加密
        UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, id));
        if (userInfo == null) {
            throw new ZsdException("用户不存在", 1002);
        }
        String newEncryptedPassword = MD5Util.md5(bean.getNewLoginPwd(), userInfo.getLoginName());
        userInfoMapper.update(new UserInfo().setPassword(newEncryptedPassword), new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, id));


    }

}